﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using KuiHuaBaoDian.Services.Guide;
using UnityEditorInternal;
using UnityEngine;

namespace KuiHuaBaoDian.GuideGraph.Editor {

    public static class GuideGraphTypeUtility {

        private static readonly Regex ASSEMBLY_NAME_REGEXP = new("\"name\": \"(.*?)\"");
        private static string LIBRARY_PATH;

        public static IEnumerable<Type> GetTypeEnumerable(Type[] targetTypes, params Type[] excludeTypes) {
            return GetTypeEnumerable(GuideSetting.Instance.AssemblyDefinitionAssets, targetTypes, excludeTypes);
        }

        public static IEnumerable<Type> GetEditorTypeEnumerable(Type[] targetTypes) {
            return GetTypeEnumerable(GuideSetting.Instance.EditorAssemblyDefinitionAssets, targetTypes);
        }

        public static IEnumerable<Type> GetTypeEnumerable(AssemblyDefinitionAsset[] assemblyDefinitionAssets, Type[] targetTypes, params Type[] excludeTypes) {
            foreach (var targetType in targetTypes) {
                foreach (var t in targetType.Assembly.GetTypes()) {
                    if (IsValidType(t, targetTypes, excludeTypes)) {
                        yield return t;
                    }
                }
            }
            LIBRARY_PATH ??= Path.Combine(Path.GetDirectoryName(Application.dataPath), "Library", "ScriptAssemblies");
            foreach (var definition in assemblyDefinitionAssets) {
                var match = ASSEMBLY_NAME_REGEXP.Match(definition.text);
                if (match.Success) {
                    var assemblyName = match.Groups[1];
                    var assemblyPath = Path.Combine(LIBRARY_PATH, $"{assemblyName}.dll");
                    if (File.Exists(assemblyPath)) {
                        var assembly = Assembly.LoadFile(assemblyPath);
                        var types = assembly.GetTypes();
                        foreach (var t in types) {
                            if (IsValidType(t, targetTypes, excludeTypes)) {
                                yield return t;
                            }
                        }
                    }
                }
            }
        }

        private static bool IsValidType(Type type, Type[] targetTypes, Type[] excludeTypes) {
            if (!type.IsAbstract && !type.IsInterface && !type.IsGenericTypeDefinition && targetTypes.All(targetType => targetType.IsAssignableFrom(type)) && !excludeTypes.Any(excudeType => excudeType.IsAssignableFrom(type))) {
                return true;
            }
            return false;
        }
    }
}
